/** @file    src/framework/building/Window.cpp
 *  @author  Alessandro Polo
 *  @version $Id: Window.cpp 2829 2010-08-02 20:45:47Z alex $
 *  @brief
 * File containing methods for the wosh::building::Window class.
 * The header for this class can be found in Window.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/building/Window.h>

 #include <core/ObjectAllocator.h>
 #include <core/MethodsCommon.h>
 #include <core/List.h>


namespace wosh {
 namespace building {

 WOSH_REGISTER(wosh::building::Window, "wosh::building::BuildingObject", _Window_VERSION, _static_Window )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

Window::Window()
	: BuildingObject() {
	BuildingObject::setName( "wosh.building.Window", _GET_Abs(Utilities::randomLong()) );
 }

void Window::init() {
	Log(LOG_DEBUG, " Setting default properties and permissions.." );

	this->state = Window::WINDOW_UNKNOWN;
	setState(Window::WINDOW_UNKNOWN, false);

	Log(LOG_DEBUG, " Registering methods.." );
	MethodWRESULTwSecurityToken* mmOpen = Methods.create<MethodWRESULTwSecurityToken>( _Window_METHOD_open, "open window", Permission::R_XR_XR__() );
	mmOpen->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&Window::mmDoOpen );

	MethodWRESULTwSecurityToken* mmClose = Methods.create<MethodWRESULTwSecurityToken>( _Window_METHOD_close, "close window", Permission::R_XR_XR__() );
	mmClose->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&Window::mmDoClose );

	MethodWRESULTwSecurityToken* mmSlideUp = Methods.create<MethodWRESULTwSecurityToken>( _Window_METHOD_slide_up, "slide up", Permission::R_XR_XR__() );
	mmSlideUp->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&Window::mmDoSlideUp );

	MethodWRESULTwSecurityToken* mmSlideDown = Methods.create<MethodWRESULTwSecurityToken>( _Window_METHOD_slide_down, "slide down", Permission::R_XR_XR__() );
	mmSlideDown->setMethod( this, (MethodWRESULTwSecurityTokenPtr)&Window::mmDoSlideDown );

	MethodMessageResponse* mmSlide = Methods.create<MethodMessageResponse>( _Window_METHOD_slide, "slide", Permission::R_XR_XR__() );
	mmSlide->setMethod( this, (MethodMessageResponsePtr)&Window::mmDoSlide );

	BuildingObject::init();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////// PROCESS CHILD MESSAGES

void Window::busMessageChild( const Message& childMessage ) {
	if ( childMessage.isEmpty() ) return;
	if ( !childMessage.getContent()->isFact() ) return;
	const Fact* fact = childMessage.getContent()->asFact();
	if ( fact == NULL ) return;

	if ( fact->getEvent() == _Device_EVENT_StateChanged ) {
		if ( !fact->hasData() ) {
			Log(LOG_WARNING, ":busMessageChild() EVENT 'StateChanged' Invalid, no Content!" );
			return;
		 }
		const Variant* data = NULL;
		if ( fact->getData()->isKindOf<Variant>() )
			data = dynamic_cast<const Variant*>( fact->getData() );
		if ( data == NULL ) {
			Log(LOG_WARNING, ":busMessageChild() EVENT 'StateChanged' Invalid, Not Data-Content!" );
			return;
		 }
		int value = data->toInteger(-1);
		Log(LOG_INFO, ":busMessageChild() EVENT 'StateChanged' => %d [%s]", value, data->getTypeAsString() );

//		Properties.update( _Appliance_KEY_Level, data->toInteger() );
		if ( value == 0 )
			setState(Window::WINDOW_CLOSED, true, childMessage.getSecurityToken());
		else if ( value == 100 )
			setState(Window::WINDOW_OPENED, true, childMessage.getSecurityToken());
		else
			setState(Window::WINDOW_UNKNOWN, true, childMessage.getSecurityToken());
	 }
	else
		Log(LOG_WARNING, ":busMessageChild() UNKNOWN EVENT '%s'", fact->getEvent().c_str() );

 }

////////////////////////////////////////////////////////////////////////////////////// PROCESS CHILD MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS

void Window::setState( Window::WINDOW_STATE new_state, bool raise_event, const SecurityToken* source_token ) {
	Window::WINDOW_STATE old_state = this->state;
	Properties.transactionBeginWrite();
	if ( this->state != new_state ) {
		this->state = new_state;
		Properties.update( _BuildingObject_KEY_State, getStateAsString() );
	 }
	Properties.update( _BuildingObject_KEY_StateTs, new Variant(Utilities::std_time(), Variant::DATETIME) );
	Properties.transactionEnd();
	if ( this->raiseAlwaysEvents || raise_event ) {
		Log(LOG_VERBOSE, ":setState(%s) Raising Event.. ", getStateAsString() );
		BuildingObject::raiseEvent( new Window_StateChanged(new_state, old_state), source_token );
	 }
	else
		Log(LOG_DEBUG, ":setState(%s) Not Raising Event.", getStateAsString() );
 }

//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// DEVICE METHODS

WRESULT Window::mmDoOpen( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoOpen()" );
	// state update is asynch. raised by Child-Device (as Fact)
	Request* request = new Request( _Switch_METHOD_on );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getMainSwitchName(), request, security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT Window::mmDoClose( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoClose()" );
	// state update is asynch. raised by Child-Device (as Fact)
	Request* request = new Request( _Switch_METHOD_off );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getMainSwitchName(), request, security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT Window::mmDoSlideUp( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoSlideUp()" );
	// state update is asynch. raised by Child-Device (as Fact)
	Request* request = new Request( _Switch_METHOD_on );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getMainSwitchName(), request, security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT Window::mmDoSlideDown( const SecurityToken* security ) {
	Log(LOG_INFO, ":mmDoSlideDown()" );
	// state update is asynch. raised by Child-Device (as Fact)
	Request* request = new Request( _Switch_METHOD_off );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getMainSwitchName(), request, security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT Window::slide( int value, const SecurityToken* security ) {
	Log(LOG_INFO, ":slide(%d)", value );
	// state update is asynch. raised by Child-Device (as Fact)
	Request* request = NULL;
	if ( value < 0 )
		request = new Request(_Dimmer_METHOD_dim, new Variant(value) );
	else
		request = new Request(_Dimmer_METHOD_bright, new Variant(value) );
	request->setOmitResponse(true);
	BuildingObject::controlChild( getMainSwitchName(), request, security );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* Window::mmDoSlide( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();

	int value = request->getArgument().toInteger(0);
	Log(LOG_INFO, ":mmDoSlide(%d)", value );

	WRESULT ret = slide( value, requestMessage->getSecurityToken() );
	return request->replyResponse(ret);
 }

////////////////////////////////////////////////////////////////////////////////////////////// DEVICE METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool Window::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = BuildingObject::updateHash( hash_context );
	ret = hash_context->update( this->state ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

const char* WindowStateStr[] = {
	"UNKNOWN",	// (WINDOW_STATE)0
	"OPENED",	// (WINDOW_STATE)1
	"CLOSED"	// (WINDOW_STATE)2
 };

Enum2StrFromArray(Window::getStateAsString, Window::WINDOW_STATE, WINDOW_OPENED, WINDOW_CLOSED, 0, WindowStateStr)

Str2EnumByArray(Window::getStateFromString, Window::WINDOW_STATE, 0, WINDOW_CLOSED, WINDOW_UNKNOWN, WindowStateStr)


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Window::WINDOW_STATE intToWindowStateEnum( int value ) {
	if ( value <= Window::WINDOW_UNKNOWN ) return Window::WINDOW_UNKNOWN;
	if ( value > Window::WINDOW_CLOSED ) return Window::WINDOW_UNKNOWN;
	return (Window::WINDOW_STATE)value;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::building::Window_StateChanged, "wosh::Fact", 1.0, _static_Window_StateChanged )

Window_StateChanged::Window_StateChanged( Window::WINDOW_STATE current_door_state )
	: wosh::Fact( _Window_EVENT_WindowStateChanged ) {
	setData( new Variant(current_door_state) );
 }

Window_StateChanged::Window_StateChanged( Window::WINDOW_STATE current_window_state, Window::WINDOW_STATE previous_window_state )
	: wosh::Fact( _Window_EVENT_WindowStateChanged ) {
	List* args = List::createFrom<Variant>( (int)current_window_state, (int)previous_window_state );
	setData(args);
 }

Window::WINDOW_STATE Window_StateChanged::getWindowState() const {
	const Variant* var = getDataItem<Variant>(0);
	if ( var == NULL ) return Window::WINDOW_UNKNOWN;
	if ( var->isNumeric() ) return Window::WINDOW_UNKNOWN;
	return intToWindowStateEnum(var->toInteger( (int)Window::WINDOW_UNKNOWN ));
 }

Window::WINDOW_STATE Window_StateChanged::getPreviousWindowState() const {
	const Variant* var = getDataItem<Variant>(1);
	if ( var == NULL ) return Window::WINDOW_UNKNOWN;
	if ( var->isNumeric() ) return Window::WINDOW_UNKNOWN;
	return intToWindowStateEnum(var->toInteger( (int)Window::WINDOW_UNKNOWN ));
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////




 }; // namespace building
}; // namespace wosh
